#include "rules.h"
#include "units.h"
#include "init.h"
#include "game.h"
#include <queue>
#include <cmath>



bool equal( double a, double b )
{
	if ( ( ( a + 0.001 ) > b ) && ( ( a - 0.001 ) < b ) )
		return true;
	else
		return false;
};



bool greater( double a, double b )
{
	if ( ( a + 0.001 ) > b )
		return true;
	else
		return false;
};



bool test_fire( unit* u, unsigned int x, unsigned int y )
{
	mapa* mp = mapa::get_instance();
	unit* targ = dynamic_cast< unit* >( mp->get_object( x, y ) );
	if ( targ == 0 )
		return false;
	if ( u->get_player() == ( targ->get_player() ) )
		return false;
	unsigned int d = floor( sqrt( pow ( ( float )( ( int )u->get_x() - ( int )x ), 2 ) + pow ( ( float )( ( int )u->get_y() - ( int )y ), 2 ) ) + 0.5 );
	if ( ( d > ( u->get_range() ) ) || ( u->get_player()->get_time() < ( int )u->get_cost_fire() ) )
		return false;
	double dx = abs( ( double )x - ( double )u->get_x() );
	double dy = abs( ( double )y - ( double )u->get_y() );
	if ( dx == 0 )
	{
		dy = 1;
	}
	else if ( dy == 0 )
	{
		dx = 1;
	}
	else
	{
		if ( dx > dy )
		{
			dy = dy / dx;
			dx = 1;
		}
		else
		{
			dx = dx / dy;
			dy = 1;	
		}
	}
	double cx = 0;
	double cy = 0;
	unsigned int fx = u->get_x();
	unsigned int fy = u->get_y();
	int px;
	int py;
	if ( fx > x )
		px = -1;
	else
		px = 1;
	if ( fy > y )
		py = -1;
	else
		py = 1;
	bool move = false;
	unsigned int test_d = 0;
	while ( !( fx == x && fy == y ) )
	{
		if ( test_d >= mapa::get_instance()->get_size_x() + mapa::get_instance()->get_size_y() )
			return false;
		if ( ( mp->terrain->get( fx, fy ) > 20000 ) || ( ( dynamic_cast< unit* >( mp->get_object( fx, fy ) ) != 0 ) && ( ( ( fx != x ) || ( fy != y ) ) && ( ( fx != u->get_x() ) || ( fy != u->get_y() ) ) ) ) )
			return false;
		if( !move )
		{
			cx += dx;
			cy += dy;
		}
		move = false;
		if ( greater( cx, 0.5 ) )
		{
			--cx;
			fx += px;
			++test_d;
			move = true;
		}
		else if ( greater( cy, 0.5 ) )
		{
			--cy;
			fy += py;
			++test_d;
			move = true;
		}
	}
	return true;
};



void get_path( std::queue< std::pair< unsigned int, unsigned int > >* path, array2d< unsigned int >& d, unsigned int sx, unsigned int sy, unsigned int dx, unsigned int dy )
{
	mapa* mp = mapa::get_instance();
	unsigned int x = dx;
	unsigned int y = dy;
	path->push( std::make_pair< unsigned int, unsigned int >( x, y ) );
	unsigned int di = d.get( x, y );
	while ( ( x != sx ) || ( y != sy ) )
	{
		di -= ( ( mp->terrain->get( x, y ) / 10 ) * 10 );
		if ( ( x > 0 ) && ( di == d.get( x - 1, y ) ) )
			--x;
		else if ( ( x < mapa::get_instance()->get_size_x() - 1 ) && ( di == d.get( x + 1, y ) ) )
			++x;
		else if ( ( y > 0 ) && ( di == d.get( x, y - 1 ) ) )
			--y;
		else if ( ( y < mapa::get_instance()->get_size_y() - 1 ) && ( di == d.get( x, y + 1 ) ) )
			++y;
		path->push( std::make_pair< unsigned int, unsigned int >( x, y ) );
	}	
};



int distance( std::queue< std::pair< unsigned int, unsigned int > >* path, unsigned int sx, unsigned int sy, unsigned int dx, unsigned int dy )
{
	mapa* mp = mapa::get_instance();
	array2d< unsigned int > dist( mapa::get_instance()->get_size_x(), mapa::get_instance()->get_size_y() );
	for ( unsigned int i = 0; i < mapa::get_instance()->get_size_x(); ++i )
		for ( unsigned int j = 0; j < mapa::get_instance()->get_size_y(); ++j )
			dist.set( 30000, i, j );
	dist.set( 0, sx, sy );
	std::queue< std::pair< unsigned int, unsigned int > > fronta;
	fronta.push( std::make_pair< unsigned int, unsigned int >( sx, sy ) );
	unsigned int x;
	unsigned int y;	
	while ( !fronta.empty() )
	{
		x = fronta.front().first;
		y = fronta.front().second;
		fronta.pop();
		if ( ( x > 0 ) && ( mp->terrain->get( x - 1, y ) <= 100) && ( ( dynamic_cast< unit* >( mp->get_object( x - 1, y ) ) ) == 0 ) )
		{
			if ( dist.get( x - 1, y ) > dist.get( x, y ) + ( ( mp->terrain->get( x - 1, y ) / 10 ) * 10 ) )
			{
				dist.set( dist.get( x, y ) + ( ( mp->terrain->get( x - 1, y ) / 10 ) * 10 ), x - 1, y );
				fronta.push( std::make_pair< unsigned int, unsigned int >( x - 1, y ) );
			}		
		}
		if ( ( x < mapa::get_instance()->get_size_x() - 1 ) && ( mp->terrain->get( x + 1, y ) <= 100 ) && ( ( dynamic_cast< unit* >( mp->get_object( x + 1, y ) ) ) == 0 ) )
		{
			if ( dist.get( x + 1, y ) > dist.get( x, y ) + ( ( mp->terrain->get( x + 1, y ) / 10 ) * 10 ) )
			{
				dist.set( dist.get( x, y ) + ( ( mp->terrain->get( x + 1, y ) / 10 ) * 10 ), x + 1, y );
				fronta.push( std::make_pair< unsigned int, unsigned int >( x + 1, y ) );
			}
		}
		if ( ( y > 0 ) && ( mp->terrain->get( x, y - 1 ) <= 100 ) && ( ( dynamic_cast< unit* >( mp->get_object( x, y - 1 ) ) ) == 0 ) )
		{
			if ( dist.get( x, y - 1 ) > dist.get( x, y ) + ( ( mp->terrain->get( x, y - 1 ) / 10 ) * 10 ) )
			{
				dist.set( dist.get( x, y ) + ( ( mp->terrain->get( x, y - 1 ) / 10 ) * 10 ), x, y - 1 );
				fronta.push( std::make_pair< unsigned int, unsigned int >( x, y - 1 ) );
			}
		}
		if ( ( y < mapa::get_instance()->get_size_y() - 1 ) && ( mp->terrain->get( x, y + 1 ) <= 100 ) && ( ( dynamic_cast< unit* > ( mp->get_object( x, y + 1 ) ) ) == 0 ) )
		{
			if ( dist.get( x, y + 1 ) > dist.get( x, y ) + ( ( mp->terrain->get( x, y + 1 ) / 10 ) * 10 ) )
			{
				dist.set( dist.get( x, y ) + ( ( mp->terrain->get( x, y + 1 ) / 10 ) * 10 ), x, y + 1 );
				fronta.push( std::make_pair< unsigned int, unsigned int >( x, y + 1 ) );
			}
		}
	}
	if ( dist.get( dx, dy ) < 30000 )
		get_path( path, dist, sx, sy, dx, dy );
	return dist.get( dx, dy );
};



int test_move( std::queue< std::pair< unsigned int, unsigned int > >* path, unit* u, unsigned int x, unsigned int y )
{
	mapa* mp = mapa::get_instance();
	if ( ( mp->terrain->get( x, y ) < 20000 ) && ( ( dynamic_cast< unit* >( mp->get_object( x, y ) ) ) == 0 ) )
	{
		unsigned int d = distance( path, u->get_x(), u->get_y(), x, y ) / 10;
		if ( ( d < 3000 ) && ( ( ( int )d * ( int )( u->get_cost_move() ) ) <= ( u->get_player()->get_time() ) ) )
			return ( ( int )d * ( ( int )u->get_cost_move() ) );
		else
			return -1;
	}
	else
		return -1;
};